home *** CD-ROM | disk | FTP | other *** search
/ Sun Solutions 1997 April to September / Sun Solutions CD - APR '97 - SEP '97 (704-3778-12 Rev. H)(Sun Microsystems, Inc.)(1997).iso / products / Hyperion / src / scsi.c < prev    next >
C/C++ Source or Header  |  1997-02-26  |  10KB  |  473 lines

  1. /*
  2.  * @(#)scsi.c    1.8    3/3/94
  3.  *
  4.  * Frontend functions for sending raw SCSI commands to the CD-ROM drive.
  5.  * These depend on wm_scsi(), which should be defined in each platform
  6.  * module.
  7.  */
  8. static char ident[] = "@(#)scsi.c    1.8\t3/3/94";
  9.  
  10. #include <stdio.h>
  11. #include "struct.h"
  12.  
  13. #define SCMD_INQUIRY        0x12
  14. #define SCMD_MODE_SELECT    0x15
  15. #define SCMD_MODE_SENSE        0x1a
  16. #define SCMD_START_STOP        0x1b
  17. #define SCMD_PREVENT        0x1e
  18. #define SCMD_READ_SUBCHANNEL    0x42
  19. #define SCMD_READ_TOC        0x43
  20. #define SCMD_PLAY_AUDIO_MSF    0x47
  21. #define SCMD_PAUSE_RESUME    0x4b
  22.  
  23. #define SUBQ_STATUS_INVALID    0x00
  24. #define SUBQ_STATUS_PLAY    0x11
  25. #define SUBQ_STATUS_PAUSE    0x12
  26. #define SUBQ_STATUS_DONE    0x13
  27. #define SUBQ_STATUS_ERROR    0x14
  28. #define SUBQ_STATUS_NONE    0x15
  29. #define SUBQ_STATUS_NO_DISC    0x17    /* Illegal, but Toshiba returns it. */
  30. #define SUBQ_ILLEGAL        0xff
  31.  
  32. #define    PAGE_AUDIO        0x0e
  33. #define LEADOUT            0xaa
  34.  
  35. extern int wm_scsi(), wmcd_open(), strncmp();
  36. extern char *getenv();
  37.  
  38. /*
  39.  * Send a SCSI command over the bus, with all the CDB bytes specified
  40.  * as unsigned char parameters.  This doesn't use varargs because some
  41.  * systems have stdargs instead and the number of bytes in a CDB is
  42.  * limited to 12 anyway.
  43.  *
  44.  * d    Drive structure
  45.  * buf    Buffer for data, both sending and receiving
  46.  * len    Size of buffer
  47.  * dir    TRUE if the command expects data to be returned in the buffer.
  48.  * a0-    CDB bytes.  Either 6, 10, or 12 of them, depending on the command.
  49.  */
  50. /*VARARGS4*/
  51. int
  52. sendscsi(d, buf, len, dir, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11)
  53.     struct wm_drive    *d;
  54.     void        *buf;        /* Buffer for command results */
  55.     unsigned int    len;        /* Size of the buffer */
  56.     int        dir;
  57.     unsigned char    a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11;
  58. {
  59.     int        cdblen = 0;
  60.     unsigned char    cdb[12];
  61.  
  62.     cdb[0] = a0;
  63.     cdb[1] = a1;
  64.     cdb[2] = a2;
  65.     cdb[3] = a3;
  66.     cdb[4] = a4;
  67.     cdb[5] = a5;
  68.  
  69.     switch ((a0 >> 5) & 7) {
  70.     case 0:
  71.         cdblen = 6;
  72.         break;
  73.     
  74.     case 5:
  75.         cdb[10] = a10;
  76.         cdb[11] = a11;
  77.         cdblen = 12;
  78.  
  79.     case 1:
  80.     case 2:
  81.     case 6:        /* assume 10-byte vendor-specific codes for now */
  82.         cdb[6] = a6;
  83.         cdb[7] = a7;
  84.         cdb[8] = a8;
  85.         cdb[9] = a9;
  86.         if (! cdblen)
  87.             cdblen = 10;
  88.         break;
  89.     }
  90.  
  91.     return (wm_scsi(d, cdb, cdblen, buf, len, dir));
  92. }
  93.  
  94. /*
  95.  * Send a MODE SENSE command and return the results (minus header cruft)
  96.  * in a user buffer.
  97.  *
  98.  * d    Drive structure
  99.  * page    Number of page to query (plus page control bits, if any)
  100.  * buf    Result buffer
  101.  */
  102. int
  103. wm_scsi_mode_sense(d, page, buf)
  104.     struct wm_drive    *d;
  105.     unsigned char    page;
  106.     unsigned char    *buf;
  107. {
  108.     unsigned char    pagebuf[255];
  109.     int        status, i, len, offset;
  110.  
  111.     status = sendscsi(d, pagebuf, sizeof(pagebuf), 1, SCMD_MODE_SENSE, 0,
  112.             page, 0, sizeof(pagebuf), 0);
  113.     if (status < 0)
  114.         return (status);
  115.     
  116.     /*
  117.      * The first byte of the returned data is the transfer length.  Then
  118.      * two more bytes and the length of whatever header blocks are in
  119.      * front of the page we want.
  120.      */
  121.     len = pagebuf[0] - pagebuf[3] - 3;
  122.     offset = pagebuf[3] + 4;
  123.     for (i = 0; i < len; i++)
  124.         buf[i] = pagebuf[offset + i];
  125.  
  126.     return (0);
  127. }
  128.  
  129. /*
  130.  * Send a MODE SELECT command.
  131.  *
  132.  * d    Drive structure
  133.  * buf    Page buffer (no need to put on block descriptors)
  134.  * len    Size of page
  135.  */
  136. int
  137. wm_scsi_mode_select(d, buf, len)
  138.     struct wm_drive    *d;
  139.     unsigned char    *buf;
  140.     unsigned char    len;
  141. {
  142.     unsigned char    pagebuf[255];
  143.     int        i;
  144.  
  145.     pagebuf[0] = pagebuf[1] = pagebuf[2] = pagebuf[3] = 0;
  146.     for (i = 0; i < (int) len; i++)
  147.         pagebuf[i + 4] = buf[i];
  148.  
  149.     return (sendscsi(d, pagebuf, len + 4, 0, SCMD_MODE_SELECT, 0x10, 0,
  150.             0, len + 4, 0));
  151. }
  152.  
  153. /*
  154.  * Send an INQUIRY command to get the drive type.
  155.  *
  156.  * d        Drive structure
  157.  * vendor    Buffer for vendor name (8 bytes + null)
  158.  * model    Buffer for model name (16 bytes + null)
  159.  * rev        Buffer for revision level (4 bytes + null)
  160.  *
  161.  * If the model name begins with "CD-ROM" and zero or more spaces, that will
  162.  * all be stripped off since it's just extra junk to WorkMan.
  163.  */
  164. int
  165. wm_scsi_get_drive_type(d, vendor, model, rev)
  166.     struct wm_drive    *d;
  167.     unsigned char    *vendor, *model, *rev;
  168. {
  169.     unsigned char        *s, *t, buf[36];
  170.  
  171.     if (sendscsi(d, buf, sizeof(buf), 1, SCMD_INQUIRY, 0, 0, 0,
  172.             sizeof(buf), 0))
  173.         return (-1);
  174.  
  175.     memcpy(vendor, buf + 8, 8);
  176.     vendor[8] = '\0';
  177.     memcpy(model, buf + 16, 16);
  178.     model[16] = '\0';
  179.     memcpy(rev, buf + 32, 4);
  180.     rev[4] = '\0';
  181.  
  182.     /* Remove "CD-ROM " from the model. */
  183.     if (! strncmp(model, "CD-ROM", 6))
  184.     {
  185.         s = model + 6;
  186.         t = model;
  187.         while (*s == ' ' || *s == '\t')
  188.             s++;
  189.         while (*t++ = *s++)
  190.             ;
  191.     }
  192.  
  193.     return (0);
  194. }
  195.  
  196. /*
  197.  * Send a SCSI-2 PAUSE/RESUME command.  "resume" is 1 to resume, 0 to pause.
  198.  */
  199. int
  200. wm_scsi2_pause_resume(d, resume)
  201.     struct wm_drive    *d;
  202.     int        resume;
  203. {
  204.     return (sendscsi(d, NULL, 0, 0, SCMD_PAUSE_RESUME, 0, 0, 0, 0, 0, 0,
  205.             0, resume ? 1 : 0, 0));
  206. }
  207.  
  208. /*
  209.  * Send a SCSI-2 "prevent media removal" command.  "prevent" is 1 to lock
  210.  * caddy in.
  211.  */
  212. int
  213. wm_scsi2_prevent(d, prevent)
  214.     struct wm_drive    *d;
  215.     int        prevent;
  216. {
  217.     return (sendscsi(d, NULL, 0, 0, SCMD_PREVENT, 0, 0, 0, 0, 0, 0,
  218.             0, prevent ? 1 : 0, 0));
  219. }
  220.  
  221. /*
  222.  * Send a SCSI-2 PLAY AUDIO MSF command.  Pass the starting and ending
  223.  * frame numbers.
  224.  */
  225. int
  226. wm_scsi2_play(d, sframe, eframe)
  227.     struct wm_drive    *d;
  228.     int        sframe, eframe;
  229. {
  230.     return (sendscsi(d, NULL, 0, 0, SCMD_PLAY_AUDIO_MSF, 0, 0,
  231.             sframe / (60 * 75), (sframe / 75) % 60, sframe % 75,
  232.             eframe / (60 * 75), (eframe / 75) % 60, eframe % 75,
  233.             0));
  234. }
  235.  
  236. /*
  237.  * Send a SCSI-2 READ TOC command to get the data for a particular track.
  238.  * Fill in track information from the returned data.
  239.  */
  240. int
  241. wm_scsi2_get_trackinfo(d, track, data, startframe)
  242.     struct wm_drive    *d;
  243.     int        track, *data, *startframe;
  244. {
  245.     unsigned char    buf[12];    /* one track's worth of info */
  246.  
  247.     if (sendscsi(d, buf, sizeof(buf), 1, SCMD_READ_TOC, 2,
  248.             0, 0, 0, 0, track, sizeof(buf) / 256,
  249.             sizeof(buf) % 256, 0))
  250.         return (-1);
  251.     
  252.     *data = buf[5] & 4 ? 1 : 0;
  253.     *startframe = buf[9] * 60 * 75 + buf[10] * 75 + buf[11];
  254.  
  255.     return (0);
  256. }
  257.  
  258. /*
  259.  * Get the starting frame for the leadout area (which should be the same as
  260.  * the length of the disc as far as WorkMan is concerned).
  261.  */
  262. int
  263. wm_scsi2_get_cdlen(d, frames)
  264.     struct wm_drive    *d;
  265.     int        *frames;
  266. {
  267.     int        tmp;
  268.  
  269.     return (wm_scsi2_get_trackinfo(d, LEADOUT, &tmp, frames));
  270. }
  271.  
  272. /*
  273.  * Get the current status of the drive by sending the appropriate SCSI-2
  274.  * READ SUB-CHANNEL command.
  275.  */
  276. int
  277. wm_scsi2_get_drive_status(d, oldmode, mode, pos, track, index)
  278.     struct wm_drive    *d;
  279.     enum cd_modes    oldmode, *mode;
  280.     int        *pos, *track, *index;
  281. {
  282.     unsigned char    buf[48];
  283.  
  284.     /* If we can't get status, the CD is ejected, so default to that. */
  285.     *mode = EJECTED;
  286.  
  287.     /* Is the device open? */
  288.     if (d->fd < 0)
  289.     {
  290.         switch (wmcd_open(d)) {
  291.         case -1:    /* error */
  292.             return (-1);
  293.         
  294.         case 1:        /* retry */
  295.             return (0);
  296.         }
  297.     }
  298.  
  299.     /* If we can't read status, the CD has been ejected. */
  300.     buf[1] = SUBQ_ILLEGAL;
  301.     if (sendscsi(d, buf, sizeof(buf), 1, SCMD_READ_SUBCHANNEL, 2, 64, 1,
  302.             0, 0, 0, sizeof(buf) / 256, sizeof(buf) % 256, 0))
  303.         return (0);
  304.     
  305.         switch (buf[1]) {
  306.         case SUBQ_STATUS_PLAY:
  307.         *mode = PLAYING;
  308.         *track = buf[6];
  309.         *index = buf[7];
  310.         *pos = buf[9] * 60 * 75 + buf[10] * 75 + buf[11];
  311.         break;
  312.  
  313.     case SUBQ_STATUS_PAUSE:
  314.         if (oldmode == PLAYING || oldmode == PAUSED)
  315.         {
  316.             *mode = PAUSED;
  317.             *track = buf[6];
  318.             *index = buf[7];
  319.             *pos = buf[9] * 60 * 75 +
  320.                 buf[10] * 75 +
  321.                 buf[11];
  322.         }
  323.         else
  324.             *mode = STOPPED;
  325.         break;
  326.  
  327.     /*
  328.      * SUBQ_STATUS_DONE is sometimes returned when the CD is idle,
  329.      * even though the spec says it should only be returned when an
  330.      * audio play operation finishes.
  331.      */
  332.     case SUBQ_STATUS_DONE:
  333.     case SUBQ_STATUS_NONE:
  334.     case SUBQ_STATUS_INVALID:
  335.         if (oldmode == PLAYING)
  336.             *mode = TRACK_DONE;
  337.         else
  338.             *mode = STOPPED;
  339.         break;
  340.  
  341.     /*
  342.      * This usually means there's no disc in the drive.
  343.      */
  344.     case SUBQ_STATUS_NO_DISC:
  345.         break;
  346.  
  347.     /*
  348.      * This usually means the user ejected the CD manually.
  349.      */
  350.     case SUBQ_STATUS_ERROR:
  351.         break;
  352.  
  353.     case SUBQ_ILLEGAL:    /* call didn't really succeed */
  354.         break;
  355.  
  356.         default:
  357.         *mode = UNKNOWN;
  358.         if (getenv("WORKMAN_DEBUG") != NULL)
  359.             printf("wm_scsi2_get_drive_status: status is 0x%x\n",
  360.                 buf[1]);
  361.         break;
  362.         }
  363.  
  364.     return (0);
  365. }
  366.  
  367. /*
  368.  * Get the number of tracks on the CD using the SCSI-2 READ TOC command.
  369.  */
  370. int
  371. wm_scsi2_get_trackcount(d, tracks)
  372.     struct wm_drive    *d;
  373.     int        *tracks;
  374. {
  375.     unsigned char    buf[4];
  376.  
  377.     if (sendscsi(d, buf, sizeof(buf), 1, SCMD_READ_TOC, 0,
  378.             0, 0, 0, 0, 0, sizeof(buf) / 256,
  379.             sizeof(buf) % 256, 0))
  380.         return (-1);
  381.  
  382.     *tracks = buf[3] - buf[2] + 1;
  383.     return (0);
  384. }
  385.  
  386. /*
  387.  * Pause the CD.
  388.  */
  389. int
  390. wm_scsi2_pause(d)
  391.     struct wm_drive    *d;
  392. {
  393.     return (wm_scsi2_pause_resume(d, 0));
  394. }
  395.  
  396. /*
  397.  * Resume playing after a pause.
  398.  */
  399. int
  400. wm_scsi2_resume(d)
  401.     struct wm_drive    *d;
  402. {
  403.     return (wm_scsi2_pause_resume(d, 1));
  404. }
  405.  
  406. /*
  407.  * Stop playing the CD by sending a START STOP UNIT command.
  408.  */
  409. int
  410. wm_scsi2_stop(d)
  411.     struct wm_drive    *d;
  412. {
  413.     return (sendscsi(d, NULL, 0, 0, SCMD_START_STOP, 0, 0,0,0,0,0,0,0,0));
  414. }
  415.  
  416. /*
  417.  * Eject the CD by sending a START STOP UNIT command.
  418.  */
  419. int
  420. wm_scsi2_eject(d)
  421.     struct wm_drive    *d;
  422. {
  423.     /* Unlock the disc (possibly unnecessary). */
  424.     if (wm_scsi2_prevent(d, 0))
  425.         return (-1);
  426.  
  427.     return (sendscsi(d, NULL, 0, 0, SCMD_START_STOP, 2, 0,0,0,0,0,0,0,0));
  428. }
  429.  
  430. /*
  431.  * Get the volume by doing a MODE SENSE command.
  432.  */
  433. int
  434. wm_scsi2_get_volume(d, left, right)
  435.     struct wm_drive    *d;
  436.     int        *left, *right;
  437. {
  438.     unsigned char    mode[16];
  439.  
  440.     *left = *right = -1;
  441.  
  442.     /* Get the current audio parameters first. */
  443.     if (wm_scsi_mode_sense(d, PAGE_AUDIO, mode))
  444.         return (-1);
  445.  
  446.     *left = ((int) mode[9] * 100) / 255;
  447.     *right = ((int) mode[11] * 100) / 255;
  448.  
  449.     return (0);
  450. }
  451.  
  452. /*
  453.  * Set the volume by doing a MODE SELECT command.
  454.  */
  455. int
  456. wm_scsi2_set_volume(d, left, right)
  457.     struct wm_drive    *d;
  458.     int        left, right;
  459. {
  460.     unsigned char    mode[16];
  461.  
  462.     /* Get the current audio parameters first. */
  463.     if (wm_scsi_mode_sense(d, PAGE_AUDIO, mode))
  464.         return (-1);
  465.     
  466.     /* Tweak the volume part of the parameters. */
  467.     mode[9] = (left * 255) / 100;
  468.     mode[11] = (right * 255) / 100;
  469.  
  470.     /* And send them back to the drive. */
  471.     return (wm_scsi_mode_select(d, mode, sizeof(mode)));
  472. }
  473.